/* Copyright (c) 2024 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "gatt_ots_client_select_object.h"
#include "gatt_ots_client_common_util.h"

/*******************************************************************************/
/*
    API to select the object at the OTS server device.
    Application should call this API only if OLCP feature & OACP -> Goto procedure features
    are set in feature supported value sent with Init CFM message.
*/
bool GattOtscSelectObjectReq(ServiceHandle svcHandle,
                             const GattOtscObjectId objectId,
                             GattOtscObjListOpcode opcode)
{
    GOTSC *gattOtsClient = ServiceHandleGetInstanceData(svcHandle);

    GATT_OTS_CLIENT_INFO("GOTSC: GattOtscSelectObjectReq: svcHandle(0x%02x) opcode[%d]\n", svcHandle, opcode);

    if (gattOtsClient)
    {
        if (gattOtsClient->pendingCmd & OTSC_PENDING_OP_OLCP_PROCEDURE)
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: Object selection already in progress!\n");
            return (FALSE);
        }
        else if (!opcode)
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: No selection opcode passed!\n");
            return (FALSE);
        }
        else if (opcode   == GATT_OTSC_OBJ_LIST_OP_GOTO &&
                 objectId == OTSC_INVALID_OBJECT_ID)
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: No Object Id for Goto opcode!\n");
            return (FALSE);
        }
        else
        {
            OtscInternalMsgSelectObject *message = CsrPmemZalloc(sizeof(*message));

            gattOtsClient->pendingCmd |= OTSC_PENDING_OP_OLCP_PROCEDURE;
            gattOtsClient->opcode      = opcode;

            message->svcHandle = gattOtsClient->srvcElem->service_handle;
            message->opcode    = opcode;

            if (opcode == GATT_OTSC_OBJ_LIST_OP_GOTO)
            {
                CsrMemCpy(message->objectId,
                          (GattOtscObjectId *)objectId,
                          GATT_OTSC_OBJ_SIZE);
            }

            GattOtscMessageSend(gattOtsClient->libTask,
                                OTSC_INTERNAL_MSG_SELECT_OBJ_REQ,
                                message);
            return (TRUE);
        }
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: Invalid OTS Client instance!\n");
        return (FALSE);
    }
}

void OtscHandleInternalMsgSelectObject(GOTSC *gatOtsClient,
                                       OtscInternalMsgSelectObject* msg)
{
    uint8* value = NULL;
    uint8  valueSize = sizeof(GattOtscObjListOpcode);

    if (msg->opcode == GATT_OTSC_OBJ_LIST_OP_GOTO)
    {
        /* object selection through object ID requested */
        valueSize = valueSize + GATT_OTSC_OBJ_SIZE;
        value     = CsrPmemZalloc(valueSize);

        /* prepare the body; selection opcode + object ID */
        value[0]  = msg->opcode;
        CsrMemCpy(&value[1], (uint8*)&msg->objectId, GATT_OTSC_OBJ_SIZE);
    }
    else
    {
        /* object selection through object procedure requested */
        value    = CsrPmemZalloc(valueSize);

        /* prepare the body; only selection opcode */
        value[0] = msg->opcode;
    }

    CsrBtGattWriteReqSend(gatOtsClient->srvcElem->gattId,
                          gatOtsClient->srvcElem->cid,
                          gatOtsClient->deviceData.objListControlPointHandle,
                          0,
                          valueSize,
                          value);
}

void OtscSendSelectObjectCfm(GOTSC *const gattOtsClient,
                             GattOtscResultCode result,
                             CsrBtSupplier supplier)
{
    GattOtscSelectObjectCfm *message = CsrPmemZalloc(sizeof(*message));

    message->svcHandle  = gattOtsClient->srvcElem->service_handle;
    message->resultCode = result;
    message->supplier   = supplier;

    gattOtsClient->pendingCmd &= ~OTSC_PENDING_OP_OLCP_PROCEDURE;

    GATT_OTS_CLIENT_INFO("GOTSC: OtscSendSelectObjectCfm: svcHandle(0x%02x)\n", gattOtsClient->srvcElem->service_handle);

    /* Send the confirmation message to app task  */
    GattOtscMessageSend(gattOtsClient->appTask, GATT_OTSC_SELECT_OBJECT_CFM, message);
}

void OtscHandleWriteCfmToSelectObject(GOTSC *const gattOtsClient,
                                      const CsrBtGattWriteCfm *cfm)
{
    if (cfm->resultCode == CSR_BT_GATT_RESULT_SUCCESS &&
        cfm->resultSupplier == CSR_BT_SUPPLIER_GATT)
    {
        /* Start OLCP timer and wait for OLCP indication */
        OtscStartOlcpTimer(gattOtsClient);
    }
    else
    { /* Operation failed */
        /* Inform app about the failure */
        OtscSendSelectObjectCfm(gattOtsClient, cfm->resultCode, cfm->resultSupplier);
    }
}

void OtscHandleObjListControlPointInd(GOTSC *const gattOtsClient,
                                      const CsrBtGattClientIndicationInd *ind)
{
    GattOtscResultCode result = GATT_OTSC_RESULT_CODE_UNKNOWN;

    if (ind->valueLength > 0)
    {
        uint8 responseCode    = ind->value[0];
        uint8 requestedOpcode = ind->value[1];
        uint8 resultCode      = ind->value[2];

        if (responseCode == GATT_OTSC_OBJ_LIST_OP_RESPONSE_CODE)
        {
            if (gattOtsClient->opcode == GATT_OTSC_OBJ_LIST_OP_SORT_ORDER    ||
                gattOtsClient->opcode == GATT_OTSC_OBJ_LIST_OP_NO_OF_OBJECT  ||
                gattOtsClient->opcode == GATT_OTSC_OBJ_LIST_OP_CLR_MARKING)
            {
                /* TODO : special handling required for above opcodes */
            }
            else
            {  /* Single CFM message to send for selection opcodes */
               /* Map the remote's result code with local OTS result code */
                switch (resultCode)
                {
                    case 0x01:
                    {
                        result = GATT_OTSC_RESULT_CODE_SUCCESS;
                        break;
                    }
                    case 0x02:
                    {
                        result = GATT_OTSC_RESULT_CODE_OPCODE_NOT_SUPPORTED;
                        break;
                    }
                    case 0x03:
                    {
                        result = GATT_OTSC_RESULT_CODE_INVALID_PARAMETER;
                        break;
                    }
                    case 0x04:
                    {
                        result = GATT_OTSC_RESULT_CODE_OPERATION_FAILED;
                        break;
                    }
                    case 0x05:
                    {
                        result = GATT_OTSC_RESULT_CODE_OUT_OF_BOUNDS;
                        break;
                    }
                    case 0x06:
                    {
                        result = GATT_OTSC_RESULT_CODE_TOO_MANY_OBJECTS;
                        break;
                    }
                    case 0x07:
                    {
                        result = GATT_OTSC_RESULT_CODE_NO_OBJECT;
                        break;
                    }
                    case 0x08:
                    {
                        result = GATT_OTSC_RESULT_CODE_OBJECT_ID_NOT_FOUND;
                        break;
                    }
                    default:
                    {
                        break;
                    }
                }
            }
        }
        else
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: OLCP ind received for unknown response code[0x%x]!\n", responseCode);
        }

        CSR_UNUSED(requestedOpcode);
    }

    if (gattOtsClient->timerId)
    {
        CsrSchedTimerCancel(gattOtsClient->timerId, NULL, NULL);
        gattOtsClient->timerId = 0;
    }

    /* Send object selection cfm to application */
    OtscSendSelectObjectCfm(gattOtsClient, result, GATT_SUPPLIER_OTS_CLIENT);
}


